/**
 * 复合页相关操作。
 * 
 * 里面的操作均不会主动对内存页上锁。
 * 
 * 复合页由多个属于同一内存区域的连续内存页组成，由首页和尾页组成，其中首页存放复合页相关的属性
 * ，所有尾页内均有指向其首页的指针。我们不能保证复合页内每个内存页的属性都完全一致，但原则上
 * 应该如此，并且使用者应当尽量将需要作为多个单独页使用的复合页进行分割，以防止出现一个复合页
 * 内有多个不同属性的内存页。
 * 
 * 复合页不能跨内存区域，且不能包含无效页。对于一个复合页的所有操作都应该在复合页的首页上进行。
 * 
 * 一个复合页包含的普通页数量为复合页的大小。复合页的大小必须为 2^k ，其中 k 应该大于0且小于
 * PAGE_MAX_ORDER。复合页首页的页帧号同样页需要按照其大小对齐。
 * 
 * 复合页当前为实现伙伴系统的基础之一。
 */
#ifndef _AHAMOLETT_COMPOUND_PAGES_H_
#define _AHAMOLETT_COMPOUND_PAGES_H_

#include <mm/mm_types.h>

/**
 * @brief 创建一个复合页。
 * 
 * 将给定的页作为首页，创建一个大小的阶数为 order 的复合页。
 * 
 * 在以下情况下会创建失败：
 * -    给定 page 的页帧号没按照 order 对齐。
 * -    order 等于0或者 order 大于等于 PAGE_MAX_ORDER。
 * -    从给定 page 开始的 2^order 个页内，存在不可被融合、无效、属于其他内存区域、或者
 *      已经存在的复合页。
 * 
 * @param page 复合页中作为首页的内存页。
 * @param order 复合页大小的阶数。
 * 
 * @return struct page_struct* 创建成功返回新创建复合页的地址，失败返回NULL。
 */
extern struct page_struct *create_compound_page(
    struct page_struct *page,
    unsigned long order
);

/**
 * @brief 计算某个页帧号的页在给定阶数下伙伴页的页帧号。
 * 
 * 该函数仅仅计算，不判断输入或者结果的有效性。
 * 
 * @param pfn 复合页的页帧号。
 * @param order 复合页大小的阶数。
 * 
 * @return unsigned long 伙伴页的页帧号。
 */
static inline unsigned long __get_compound_buddy_pfn(
    unsigned long pfn, 
    unsigned long order
)
{
    return pfn ^ (1UL << order);
}

/**
 * @brief 计算某个页帧号的页在给定阶数下伙伴页的页结构体。
 * 
 * 该函数仅仅计算，不判断输入或者结果的有效性。
 * 
 * @param page 复合页首页的结构体。
 * @param order 复合页大小的阶数。
 * 
 * @return struct page_struct* 伙伴页的页结构体。
 */
extern struct page_struct *__get_compound_buddy(
    struct page_struct *page, 
    unsigned long order
);

/**
 * @brief 将一个复合页二分。
 * 
 * 将一个复合页一分为二，并返回分裂得到的另一个复合页首页的结构体。
 * 
 * 如果传入的页不为一个复合页的首页，或者其他不能进行分裂的页，则分裂失败。
 * 
 * @param page 进行分裂的复合页的首页。
 * 
 * @return struct page_struct* 分裂成功返回分裂得到的另一个复合页首页的结构体，
 *                             失败返回NULL。
 */
extern struct page_struct *split_compound_page(struct page_struct *page);

/**
 * @brief 融合一对可被融合的组合页。
 * 
 * 传入的必须为两个单页或复合页的首页。
 * 
 * @param page1 单页或复合页的首页。
 * @param page2 单页或复合页的首页。
 * 
 * @return struct page_struct* 合并成功返回合并得到的复合页的首页的结构体，
 *                             失败返回NULL。
 */
extern struct page_struct *merge_pages(
    struct page_struct *page1, 
    struct page_struct *page2
);

/**
 * @brief 将一个复合页完全拆分成多个单页。
 * 
 * 传入的页必须为一个复合页的首页，否则拆分失败。
 * 
 * @param page 进行分裂的复合页的首页。
 * 
 * @return unsigned long 分裂成功返回分裂得到的单页个数，失败返回0。
 */
extern unsigned long break_cmpound_page(struct page_struct *page);

/**
 * @brief 判断两个复合页是否可融合。
 * 
 * 传入的必须为两个复合页的首页。两个复合页必须有同样的阶数，相邻，位于同一个内存区域，且满足
 * 其他相关条件才能被判断为可被融合。
 * 
 * @param page1 复合页的首页。
 * @param page2 复合页的首页。
 * 
 * @retval true 两个复合页可被融合。
 * @retval false 两个复合页不可被融合。
 */
extern int are_pages_mergeable(
    struct page_struct *page1, 
    struct page_struct *page2
);

/**
 * @brief 判断给定页是否为复合页。
 * 
 * 这个函数只判断传入的页是否为有效的复合页，可以传入首页。
 * 
 * @param page 内存页结构体。
 * 
 * @retval true 是复合页。
 * @retval false 不是复合页。
 */
extern int is_compound_page(struct page_struct *page);

/**
 * @brief 判断给定页是否为复合页的首页。
 * 
 * 该函数不仅会判断传入的页是否为复合页，还会判断传入的页是否为复合页的首页。
 * 
 * @param page 内存页结构体。
 * 
 * @retval true 是复合页的首页。
 * @retval false 不是复合页的首页。
 */
extern int is_page_header(struct page_struct *page);

/**
 * @brief 获得一个复合页的首页。
 * 
 * 传入一个页结构体，如果该页属于某个复合页返回对应复合页的首页，如果这个页不属于某个复合页或者
 * 无效，返回NULL。
 * 
 * @param page 内存页结构体。
 * 
 * @return struct page_struct* 获得成功返回首页的结构体指针，失败返回NULL。
 */
extern struct page_struct *get_header_page(struct page_struct *page);

/**
 * @brief 获得一个复合页大小的阶数。
 * 
 * 传入一个复合页的首页，返回其大小的阶数。
 * 
 * @param page 复合页首页的结构体。
 * 
 * @return unsigned long 传入的页为复合页的首页时返回对应的阶数，否则返回0。
 */
extern unsigned long get_page_order(struct page_struct *page);

/**
 * @brief 获得一个内存区域内首个复合页的首页或单页。
 * 
 * 用于遍历内存区域内的所有单页与复合页。
 * 
 * @param zone 进行遍历的内存区域结构体。
 * 
 * @return struct page_struct* 存在合法的复合页或单页返回首个复合页的首页或单页，
 *                             否则返回NULL。
 */
extern struct page_struct *get_first_compound_page(struct zone_struct *zone);

/**
 * @brief 获得给定页结构体之后首个复合页的首页或单页。
 * 
 * 用于遍历内存区域内的所有单页与复合页。
 * 
 * @param page 复合页的首页或单页。
 * 
 * @return struct page_struct* 存在下一个合法的复合页或单页返回首个复合页的首页或单页，
 *                             否则返回NULL。
 */
extern struct page_struct *get_next_compound_page(struct page_struct *page);

/**
 * @brief 遍历内存区域内的所有单页与复合页。
 * 
 * 对 for 循环的封装，用于遍历给定内存区域内的所有单页与复合页。
 * 
 * @param zone struct zone_struct* 进行遍历的内存区域结构体。
 * @param pos struct page_struct* 作为游标储存当前遍历的内存页结构体。
 * @param n struct page_struct* 储存下一个进行遍历的内存页结构体。
 * @param order unsigned long 储存当前内存页的阶数。
 */
#define for_each_compound_page(zone, pos, n, order)   \
    for (   (pos) = get_first_compound_page(zone),  \
            (n) = get_next_compound_page(pos),  \
            (order) = get_page_order(pos);  \
            (pos) != NULL;  \
            (pos) = (n),    \
            (n) = get_next_compound_page(pos),  \
            (order) = get_page_order(pos)   \
    )


#endif
